<template>
  <div class="upload-file-component">
    <!-- 文件上传组件 -->
    <el-upload
      ref="uploadRef"
      :action="action"
      :auto-upload="autoUpload"
      :multiple="multiple"
      :limit="limit"
      :file-list="fileList"
      :list-type="listType"
      :http-request="customUpload"
      :before-upload="beforeUpload"
      :on-preview="handlePreview"
      :on-remove="handleRemove"
      :on-exceed="handleExceed"
      :on-success="handleSuccess"
      :on-error="handleError"
      :disabled="disabled"
      :accept="accept"
      :show-file-list="showFileList"
      :drag="drag"
      :class="{ 'hide-upload-button': fileList.length >= limit && !multiple }"
    >
      <template v-if="drag">
        <el-icon class="el-icon--upload"><upload-filled /></el-icon>
        <div class="el-upload__text">
          将文件拖到此处，或<em>点击上传</em>
        </div>
      </template>
      <template v-else>
        <!-- <el-button type="primary" :disabled="disabled">
          {{ buttonText }}
        </el-button> -->
        <el-icon class="el-icon--upload"><Plus /></el-icon>

      </template>
      <template #tip>
        <div class="el-upload__tip" v-if="showTip">
          {{ tipText }}
        </div>
      </template>
    </el-upload>

    <!-- 图片预览对话框 -->
    <el-dialog v-model="previewVisible" title="图片预览" width="800px">
      <img :src="previewUrl" alt="Preview Image" style="width: 100%" />
    </el-dialog>
  </div>
</template>

<script setup>
import { ref, computed, watch, onMounted } from 'vue';
import { UploadFilled, Plus } from '@element-plus/icons-vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import axios from 'axios';

const props = defineProps({
  // 接口地址
  action: {
    type: String,
    default: ''
  },
  // 是否自动上传
  autoUpload: {
    type: Boolean,
    default: true
  },
  // 是否支持多选文件
  multiple: {
    type: Boolean,
    default: false
  },
  // 上传数量限制
  limit: {
    type: Number,
    default: 5
  },
  // 文件列表的类型 (text, picture, picture-card)
  listType: {
    type: String,
    default: 'picture-card'
  },
  // 使用modelValue接收v-model绑定值
  modelValue: {
    type: Array,
    default: () => []
  },
  // 是否禁用
  disabled: {
    type: Boolean,
    default: false
  },
  // 接受上传的文件类型
  accept: {
    type: String,
    default: 'image/jpeg,image/png,image/jpg,image/gif'
  },
  // 文件大小限制，单位MB
  fileSize: {
    type: Number,
    default: 2
  },
  // 是否显示已上传文件列表
  showFileList: {
    type: Boolean,
    default: true
  },
  // 是否启用拖拽上传
  drag: {
    type: Boolean,
    default: false
  },
  // 按钮文本
  buttonText: {
    type: String,
    default: '点击上传'
  },
  // 是否显示提示文本
  showTip: {
    type: Boolean,
    default: true
  },
  // 上传接口的请求头
  headers: {
    type: Object,
    default: () => ({})
  },
  // 上传接口的额外参数
  data: {
    type: Object,
    default: () => ({})
  },
  // 文件字段名
  name: {
    type: String,
    default: 'file'
  },
  // 上传请求的方法
  method: {
    type: String,
    default: 'post'
  },
  // 响应数据字段映射
  responseMap: {
    type: Object,
    default: () => ({
      url: 'url', // 默认响应中的url字段
      name: 'name', // 默认响应中的name字段
    })
  }
});

const emit = defineEmits(['update:modelValue', 'success', 'error', 'exceed', 'remove', 'preview', 'change']);

// 上传组件引用
const uploadRef = ref(null);

// 文件列表 - 只在内部使用
const fileList = ref([]);

// 预览相关
const previewVisible = ref(false);
const previewUrl = ref('');

// 提示文本
const tipText = computed(() => {
  const types = props.accept.split(',').map(type => type.split('/')[1].toUpperCase()).join('/');
  return `请上传${types}格式文件，且不超过${props.fileSize}MB，最多可上传${props.limit}个文件`;
});

// 初始化及同步外部传入的值到内部fileList
watch(() => props.modelValue, (newVal) => {
  if (!newVal || newVal.length === 0) {
    fileList.value = [];
    return;
  }
  
  fileList.value = newVal.map((item, index) => {
    // 如果是字符串URL，转为对象格式
    if (typeof item === 'string') {
      return { 
        name: `文件${index + 1}`, 
        url: item 
      };
    }
    // 如果已经是对象
    else if (typeof item === 'object') {
      // 有url就返回原对象
      if (item.url) {
        return item;
      }
      // 处理后端返回的响应数据，将其转换为组件需要的格式
      if (item.response) {
        const responseData = item.response;
        return {
          ...item,
          url: responseData[props.responseMap.url] || responseData.url || '',
          name: responseData[props.responseMap.name] || responseData.name || `文件${index + 1}`
        };
      }
    }
    return item;
  });
}, { immediate: true, deep: true });

// 图片预览
const handlePreview = (file) => {
  previewUrl.value = file.url || (file.response && file.response[props.responseMap.url]) || URL.createObjectURL(file.raw);
  previewVisible.value = true;
  emit('preview', file);
};

// 移除图片
const handleRemove = (file, uploadFiles) => {
  // 同步到父组件
  updateModelValue(uploadFiles);
  emit('remove', file, uploadFiles);
};

// 超出上传数量限制时的处理
const handleExceed = (files, uploadFiles) => {
  ElMessage.warning(`当前限制上传${props.limit}个文件，本次选择了${files.length}个文件，共上传了${uploadFiles.length + files.length}个文件`);
  emit('exceed', files, uploadFiles);
};

// 上传成功回调
const handleSuccess = (response, file, uploadFiles) => {
  ElMessage.success('上传成功');
  
  // 更新文件url (从响应中获取)
  if (response) {
    const url = response[props.responseMap.url] || response.url;
    if (url) {
      file.url = url;
    }
  }
  
  // 同步到父组件
  updateModelValue(uploadFiles);
  emit('success', response, file, uploadFiles);
};

// 上传失败回调
const handleError = (error, file, uploadFiles) => {
  ElMessage.error('上传失败');
  console.error('上传失败:', error);
  emit('error', error, file, uploadFiles);
};

// 更新modelValue的函数
const updateModelValue = (files) => {
  const valueToEmit = files.map(file => {
    // 如果已有url，直接返回url
    if (file.url) {
      return { url: file.url, name: file.name };
    }
    // 如果是刚上传的文件，从响应中获取url
    else if (file.response) {
      const url = file.response[props.responseMap.url] || file.response.url;
      const name = file.response[props.responseMap.name] || file.response.name || file.name;
      return { url, name };
    }
    // 回退方案
    return file;
  });
  
  emit('update:modelValue', valueToEmit);
  emit('change', valueToEmit);
};

// 上传前的校验
const beforeUpload = (file) => {
  // 检查文件类型
  const fileType = file.type;
  const acceptTypes = props.accept.split(',');
  const isAccept = acceptTypes.some(type => {
    // 处理mime类型带通配符的情况，如image/*
    if (type.includes('*')) {
      const mainType = type.split('/')[0];
      return fileType.startsWith(mainType);
    }
    return type === fileType;
  });
  
  if (!isAccept) {
    ElMessage.error(`文件格式不正确，请上传${acceptTypes.join('/')}格式文件`);
    return false;
  }

  // 检查文件大小
  const isLtSize = file.size / 1024 / 1024 < props.fileSize;
  if (!isLtSize) {
    ElMessage.error(`文件大小不能超过 ${props.fileSize}MB!`);
    return false;
  }

  return true;
};

// 自定义上传方法
const customUpload = async (options) => {
  const { file, onProgress, onSuccess, onError } = options;
  
  // 创建 FormData 对象，添加文件和额外数据
  const formData = new FormData();
  formData.append(props.name, file);
  
  // 添加额外数据
  Object.keys(props.data).forEach(key => {
    formData.append(key, props.data[key]);
  });

  try {
    const config = {
      headers: {
        'Content-Type': 'multipart/form-data',
        ...props.headers
      },
      onUploadProgress: (progressEvent) => {
        // 计算上传进度
        const percent = Math.round((progressEvent.loaded * 100) / progressEvent.total);
        onProgress({ percent });
      }
    };

    // 发送上传请求
    const response = await axios({
      method: props.method,
      url: props.action,
      data: formData,
      ...config
    });

    // 获取响应数据
    const responseData = response.data;
    
    // 上传成功
    onSuccess(responseData);
    return responseData;
  } catch (error) {
    // 上传失败
    onError(error);
    return Promise.reject(error);
  }
};

// 手动上传方法
const submitUpload = () => {
  if (uploadRef.value) {
    uploadRef.value.submit();
  }
};

// 清空上传列表
const clearFiles = () => {
  if (uploadRef.value) {
    uploadRef.value.clearFiles();
    fileList.value = [];
    emit('update:modelValue', []);
    emit('change', []);
  }
};

// 暴露方法供父组件调用
defineExpose({
  submitUpload,
  clearFiles,
  uploadRef
});
</script>

<style scoped>
.upload-file-component {
  width: 100%;
}

.hide-upload-button :deep(.el-upload--picture-card) {
  display: none;
}

.el-upload__tip {
  font-size: 12px;
  color: #606266;
  margin-top: 5px;
}

:deep(.el-upload-list--picture-card .el-upload-list__item),
:deep(.el-upload--picture-card) {
  width: 146px;
  height: 146px;
}
</style>
